﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BackgroundTest.BasicAbstracts;

using Microsoft.Xna.Framework;

namespace BackgroundTest.Sprites
{
    /************************************************************************/
    /* Class name: MessageTranslator                                        */
    /* Function: A static class used to provide translation service.        */
    /************************************************************************/
    public class MessageTranslator
    {
        private static MessageTranslator instance;

        public static MessageTranslator Instance
        {
            get
            {
                if (null == instance)
                {
                    instance = new MessageTranslator();
                }
                return instance;
            }
        }
        
        // The pace of each step in walking procedure.
        float[] walkPace;

        // The time of walk
        int walkTime;

        // The switch time of walk
        int walkSwitchTime;

        // The pace of each step in jumping procedure.
        Vector2[] jumpPace;

        // The time of jump;
        int jumpTime;

        // The switch time of jump
        int jumpSwithTime;

        // The basic parabola's of jump
        float parabolaHeight;

        /************************************************************************/
        /* Method name: Construction Function (Static)                          */
        /************************************************************************/
        private MessageTranslator()
        {
            walkTime = TextureActionConstants.WalkTime;
            walkPace = new float[walkTime];
            for (int i = 0; i <= walkTime - 1; i++)
            {
                walkPace[i] = Convert.ToSingle(1.00000 / walkTime * (i + 1));
            }
            walkSwitchTime = TextureActionConstants.WalkSwitchTime;
            jumpTime = TextureActionConstants.JumpTime;
            jumpPace = new Vector2[jumpTime];
            jumpSwithTime = TextureActionConstants.JumpSwitchTime;
        }

        /************************************************************************/
        /* Method name: TranslateSpriteMessage                                  */
        /* Parameters: Camera: the camera of the battle field.                  */
        /************************************************************************/
        public SpriteMessageEX TranslateSpriteMessage(SpriteMessage _message)
        {
            VectorScreen[] coordinates;
            bool deltaZ = true;
            int wflag = 1;
            int hflag = 1;
            int time;
            int switchTime;

            switch (_message.direction)
            {
                case Direction.Up:
                    hflag = -1;
                    deltaZ = true;
                    break;
                case Direction.Left:
                    wflag = -1;
                    hflag = -1;
                    deltaZ = true;
                    break;
                case Direction.Down:
                    wflag = -1;
                    deltaZ = false;
                    break;
                case Direction.Right:
                    deltaZ = false;
                    break;
                default:
                    break;
            }

            switch (_message.moveType)
            {
                case MoveType.Walk:
                    coordinates = new VectorScreen[walkTime];
                    coordinates[0] = new VectorScreen(Convert.ToInt16(walkPace[0] * BasicData.LandformWidth / 2) * wflag, Convert.ToInt16(walkPace[0] * BasicData.LandformHeight / 2) * hflag);
                    for (int i = 1; i<=walkTime-1; i++ )
                    {
                        coordinates[i] = new VectorScreen((Convert.ToInt16(walkPace[i] * BasicData.LandformWidth / 2) - Convert.ToInt16(walkPace[i - 1] * BasicData.LandformWidth / 2)) * wflag,
                            (Convert.ToInt16(walkPace[i] * BasicData.LandformHeight / 2) - Convert.ToInt16(walkPace[i - 1] * BasicData.LandformHeight / 2)) * hflag);
                    }
                    time = walkTime;
                    switchTime = walkSwitchTime;
                    break;

                case MoveType.Jump:
                    parabolaHeight = 0.5f*Math.Abs(_message.jumpHeight);
                    for (int i = 0; i <= jumpTime - 1; i++)
                    {
                        jumpPace[i].X = Convert.ToSingle(1.0000 / jumpTime * (i + 1));
                        jumpPace[i].Y = Convert.ToSingle(4.0000 * parabolaHeight / ((jumpTime - 1) * (jumpTime - 1)) * (i - jumpTime / 2) * (i - jumpTime / 2) - parabolaHeight);
                    }
                    coordinates = new VectorScreen[jumpTime];
                    int[] directCoordinate;
                    directCoordinate = new int[jumpTime];
                    for (int i = 0; i <= jumpTime - 1; i++)
                    {
                        directCoordinate[i] = (int)(1.00000 / jumpTime * (i + 1) * (BasicData.LandformHeight / 2 - _message.jumpHeight * hflag * BasicData.StairHeight)) * hflag;
                    }
                    coordinates[0] = new VectorScreen((int)(jumpPace[0].X * BasicData.LandformWidth / 2) * wflag,
                        (int)(jumpPace[0].Y * BasicData.LandformHeight / 2 + directCoordinate[0]));
                    for (int i = 1; i <= jumpTime - 1; i++)
                    {
                        coordinates[i] = new VectorScreen(((int)(jumpPace[i].X * BasicData.LandformWidth / 2) - (int)(jumpPace[i - 1].X * BasicData.LandformWidth / 2)) * wflag,
                            ((int)(jumpPace[i].Y * BasicData.LandformHeight / 2) - (int)(jumpPace[i - 1].Y * BasicData.LandformHeight / 2) + directCoordinate[i] - directCoordinate[i - 1]));
                    }
                    time = jumpTime;
                    switchTime = jumpSwithTime;
                    break;

                default:
                    coordinates = new VectorScreen[walkTime];
                    time = walkTime;
                    switchTime = walkSwitchTime;
                    break;
            }
            return new SpriteMessageEX(_message.direction, _message.moveType, coordinates, hflag == 1 ? switchTime : time - switchTime, deltaZ);
        }
    }

    /************************************************************************/
    /* Class name: SpriteMessage                                            */
    /* Function: Used to record the command to a sprite.                    */
    /************************************************************************/
    public class SpriteMessage
    {
        // The number of sprite that this message is assigned to.
        public int assignedNumber { get; set; }

        // The mark of whether the message is fully accomplished.
        public bool isAccomplished { get; set; }

        // The direction of the action
        public Direction direction { get; set; }

        // The type of move of the action
        public MoveType moveType { get; set; }

        // The height of jump
        public int jumpHeight { get; set; }

        // If the next message should be conducted simultaneously
        public bool isContinue;

        /************************************************************************/
        /* Method name: Construction Function                                   */
        /* Parameters: int = 0: The number of sprite that this message is assig-*/
        /*                      ed to.                                          */
        /*             Direction = Continue: The direction of the movement.     */
        /*             MoveType = Continue: The type of move.                   */
        /*             int = 1: If MoveType is assigned to "Jump", please fill  */
        /*                      the height of jump in this parameter.           */
        /************************************************************************/
        public SpriteMessage ( int _assignedNumber, Direction _direction, MoveType _moveType, int _jumpHeight, bool _isContinue)
        {
            if (_moveType!= MoveType.Jump)
            {
                isAccomplished = true;
            }
            assignedNumber = _assignedNumber;
            direction = _direction;
            moveType = MoveType.Jump;
            jumpHeight = _jumpHeight;
            isContinue = _isContinue;
        }
        public SpriteMessage(int _assignedNumber, Direction _direction, MoveType _moveType, bool _isContinue)
        {
            assignedNumber = _assignedNumber;
            direction = _direction;
            moveType = _moveType;
            isContinue = _isContinue;
        }
        public SpriteMessage( int _assignedNumber, Direction _direction, MoveType _moveType , int _jumpHeight ):
            this(_assignedNumber,_direction,_moveType,_jumpHeight,false)
        {
            if (_moveType != MoveType.Jump)
            {
                isAccomplished = true;
            }
        }
        public SpriteMessage( int _assignedNumber, Direction _direction, MoveType _moveType ):
            this(_assignedNumber,_direction,_moveType,false)
        {

        }
        public SpriteMessage ( int _assignedNumber, Direction _direction ): 
            this(_assignedNumber,_direction,MoveType.Continue)
        {
            
        }

    }

    /************************************************************************/
    /* Class name: SpriteMessageEX                                          */
    /* Function: The executable version of class "SpriteMessage", restored  */
    /*           and used only inside a sprite                              */
    /************************************************************************/
    public class SpriteMessageEX
    {
        // The type of move of the action
        public MoveType moveType { get; set; }

        // The direction of the move
        public Direction direction { get; set; }

        // The information of the coordinate of each step.
        public VectorScreen [] coordinateOfSteps { get; set; }

        // The chance to switch z-buffer
        public int switchTime { get; set; }

        // The value of z-buffer's increasing (true->z increases; false-> z decreases)
        public bool deltaZ { get; set; }

        // The step of current executable message
        private int step;
        public int Step
        {
            get { return step; }
        }

        // The total step of current executable message
        private int stepMax;
        public int StepMax
        {
            get { return stepMax; }
        }

        /************************************************************************/
        /* Method name: Construction Function                                   */
        /* Parameters: Direction: The direction of movement                     */
        /*             MoveType: The type of move.                              */
        /*             VectorTwoDimension []: The coordinate sequence of this   */
        /*                                    move                              */
        /*             int: the time when z changes.                            */
        /*             bool: If bool is true, then z increases.                 */
        /************************************************************************/
        public SpriteMessageEX(Direction _direction, MoveType _moveType, VectorScreen[] _coordinates, int _switchTime, bool _deltaZ)
        {
            direction = _direction;
            moveType = _moveType;
            coordinateOfSteps = _coordinates;
            switchTime = _switchTime;
            deltaZ = _deltaZ;
            step = 0;
            stepMax = coordinateOfSteps.GetLength(0);
        }

        /************************************************************************/
        /* Method name: GetNextPosition                                         */
        /* Function: Return the next delta position.                            */
        /************************************************************************/
        public VectorScreen GetNextPosition()
        {
            step += 1;
            return coordinateOfSteps[step-1];
        }

        /************************************************************************/
        /* Method name: IsFinished                                              */
        /* Function: Tell you if the message is fully executed.                 */
        /* Warning: only calling this function after GetNextPosition is legal.  */
        /************************************************************************/
        public bool IsFinished()
        {
            if (step >= stepMax)
            {
                return true;
            }
            else return false;
        }

        /************************************************************************/
        /* Method name: IsSwitch                                                */
        /* Function: Tell you if the time to switch zprecision is reached.      */
        /* Warning: only calling this function after GetNextPosition is legal.  */
        /************************************************************************/
        public bool IsSwitch()
        {
            if (step == switchTime)
            {
                return true;
            }
            else return false;
        }
    }
}
